package com.xiang.api;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.Security;
import java.text.DecimalFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.imageio.ImageIO;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.xiang.api.entity.Remark;
import com.xiang.api.entity.Rule;
import com.xiang.sql.entity.User;
import com.xiang.sql.mapper.SettingMapper;

import jakarta.servlet.http.HttpServletRequest;

public class Util {
  public static ApplicationContext applicationContext;
  public static Environment environment;
  public static final int perSize = 20;
  public static JSONObject pd = null;
  public static User pu = null;
  public static final String[] DIGITS = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九" };
  public static final String[] UNITS = { "十", "百", "千", "万", "十", "百", "千", "亿", "十", "百", "千" };
  public static final String pwd = System.getProperty("user.dir");

  public static void test() {
    mklink();
    Util.clear();
  }

  public static void print(Object... param) {
    String pString = "";
    for (Object o : param) {
      pString += String.format(" %s: %s", o == null ? "null" : o.getClass().getName(), toJString(o));
    }
    System.out.println(pString.length() > 0 ? pString.substring(1) : null);
  }

  // #region 时间处理
  public static long[] getRangeByYearMonth(int year, int month) {
    YearMonth ym = YearMonth.of(year, month);
    return new long[] {
        ym.atDay(1)
            .atStartOfDay(ZoneId.systemDefault())
            .toInstant().toEpochMilli() / 1000,
        ym.atEndOfMonth()
            .atTime(23, 59, 59).atZone(ZoneId.systemDefault())
            .toInstant().toEpochMilli() / 1000
    };
  }

  // #endregion 时间处理

  // #region 获取 ApplicationContext
  public static void setApplicationContext(ApplicationContext p) {
    if (Util.applicationContext == null) {
      Util.applicationContext = p;
      environment = p.getEnvironment();
      pu = new User(true);
      SettingMapper sm = p.getBean(SettingMapper.class);
      pd = JSONObject.parseObject(sm.selectById("permission").getValue());
    }
  }

  /** 处理本文件时刻保持为硬链接状态 */
  private static void mklink() {
    String n = "Util.java",
        mp = String.format("%s\\%s", pwd, "src\\main\\java\\com\\xiang\\api"),
        m = fileExists(String.format("%s\\%s", mp, n), true),
        s = fileExists(String.format("d:\\%s", n), true);
    if (s != null && m != null) {
      try {
        if (!Files.readString(Paths.get(s)).equals(Files.readString(Paths.get(m)))) {
          run(String.format("cd /d %s;mklink /h %s %s", mp, n, s));
        }
      } catch (Exception e) {
      }
    }
  }

  // 获取applicationContext
  public static ApplicationContext getApplicationContext() {
    return applicationContext;
  }

  public static Environment getEnvironment() {
    return environment;
  }

  // 通过name获取 Bean.
  public static Object getBean(String name) {
    return applicationContext.getBean(name);
  }

  // 通过class获取Bean.
  public static <T> T getBean(Class<T> clazz) {
    return getApplicationContext().getBean(clazz);
  }

  // 通过name,以及Clazz返回指定的Bean
  public static <T> T getBean(String name, Class<T> clazz) {
    return getApplicationContext().getBean(name, clazz);
  }

  public static String getString(String key) {
    return environment.getProperty(key);
  }

  public static int getInt(String key) {
    return NumberUtils.toInt(environment.getProperty(key));
  }

  public static long getLong(String key) {
    return NumberUtils.toLong(environment.getProperty(key));
  }

  public static boolean getBoolean(String key) {
    return BooleanUtils.toBoolean(environment.getProperty(key));
  }
  // #endregion

  // #region 文件读取
  public static void run(String cmd) throws Exception {
    String[] cmds = { cmd };
    Process p = Runtime.getRuntime().exec(cmds);
    InputStream stderr = p.getErrorStream();
    InputStreamReader isr = new InputStreamReader(stderr);
    BufferedReader br = new BufferedReader(isr);
    String line = null;

    StringBuffer sb = new StringBuffer();
    while ((line = br.readLine()) != null) {
      sb.append(line);
    }
  }

  public static boolean isImg(File input) {
    try {
      String s = ImageIO.getImageReaders(ImageIO.createImageInputStream(input)).next().getFormatName();
      return !Util.isEmpty(s);
    } catch (Exception e) {
    }
    return false;
  }

  public static String writeImg(InputStream input, String outPath, Integer width) {
    try {
      final String outFormat = "webp";
      int i = outPath.lastIndexOf('.');
      outPath = formatFilename((i > 0 ? outPath.substring(0, i) : outPath) + ".webp", true);
      final File output = new File(outPath);
      BufferedImage image = ImageIO.read(input);
      if (width != null && image.getWidth() > width) {
        final int height = (int) Math.round(image.getHeight() * (width / (double) image.getWidth()));
        BufferedImage tmp = new BufferedImage(width, height, image.getType());
        // 使用 Graphics2D 绘制缩放后的图像
        Graphics2D g2d = tmp.createGraphics();
        g2d.drawImage(image, 0, 0, width, height, null);
        g2d.dispose();
        image = tmp;
      }
      // 创建 WebP 输出流
      FileOutputStream fos = new FileOutputStream(output);
      boolean result = ImageIO.write(image, outFormat, fos);
      fos.close();
      if (result) {
        return outPath;
      }
    } catch (Exception e) {
    }

    return null;
  }

  /** 生成二维码,默认大小400 */
  public static byte[] qr(String param) {
    return qr(param, 400);
  }

  /** 生成二维码 */
  public static byte[] qr(String param, int size) {
    QRCodeWriter qrCodeWriter = new QRCodeWriter();
    try {
      BitMatrix bitMatrix = qrCodeWriter.encode(param, BarcodeFormat.QR_CODE, size, size);
      // 设置背景色为透明
      MatrixToImageConfig config = new MatrixToImageConfig(0xff000000, 0x00ffffff);
      // Path b = FileSystems.getDefault().getPath("./123.png");
      ByteArrayOutputStream b = new ByteArrayOutputStream();
      MatrixToImageWriter.writeToStream(bitMatrix, "PNG", b, config);
      return b.toByteArray();
    } catch (Exception e) {
      // e.printStackTrace();
    }
    return null;
  }

  public static String fileExists(String str, boolean isFile) {
    File fi = new File(str);
    String ret = null;
    try {
      ret = fi.getCanonicalPath();
    } catch (Exception e) {
    }
    return fi.exists() && (isFile ? fi.isFile() : true) ? ret : null;
  }

  public static String fileExists(String str) {
    return fileExists(str, false);
  }

  public static String formatFilename(String str) {
    return formatFilename(str, true);
  }

  public static String formatFilename(String str, boolean createPath) {
    File fi = new File(str);
    String ret = null;
    try {
      ret = fi.getCanonicalPath();
      if (fi.getParentFile() != null) {
        fi = fi.getParentFile();
      }
      if (createPath && !fi.exists()) {
        fi.mkdirs();
      }
    } catch (IOException e) {
      System.out.println("formatFilename出错: " + str);
    }
    return ret;
  }

  public static byte[] readFile(String fileName) {
    File file = new File(fileName);
    InputStream fi = null;
    long fileSize = file.length();
    if (fileSize > Integer.MAX_VALUE) {
      throw new RuntimeException("文件太大");
    }

    byte[] ret = new byte[(int) fileSize];
    try {
      // 一次读一个字节
      fi = new FileInputStream(file);
      int offset = 0, numRead = 0;
      while (offset < fileSize && (numRead = fi.read(ret, offset, (int) fileSize - offset)) >= 0) {
        offset += numRead;
      }
      fi.close();
      return ret;
    } catch (Exception e) {
      throw new RuntimeException("读取文件出错:" + fileName);
    }
  }

  public static String readFileToString(String fileName) {
    String ret = null;
    try {
      ret = new String(readFile(fileName));
    } catch (Exception e) {
    }
    return ret;
  }

  public static boolean writeFile(String fileName, String data) {
    return writeFile(fileName, data.getBytes());
  }

  public static boolean writeFile(String fileName, byte[] data) {
    try {
      FileOutputStream fos = new FileOutputStream(formatFilename(fileName));
      fos.write(data);
      fos.close();
      return true;
    } catch (Exception e) {
      System.out.println("写文件 错误");
      System.out.println(e);
      return false;
    }
  }

  /**
   * 根据路径删除指定的目录或文件，无论存在与否
   *
   * @param sPath 要删除的目录或文件
   * @return 删除成功返回 true，否则返回 false。
   */
  public static boolean DeleteFolder(String sPath) {
    boolean flag = false;
    File file = new File(sPath);
    // 判断目录或文件是否存在
    if (!file.exists()) { // 不存在返回 false
      return flag;
    } else {
      // 判断是否为文件
      if (file.isFile()) { // 为文件时调用删除文件方法
        return deleteFile(sPath);
      } else { // 为目录时调用删除目录方法
        return deleteDirectory(sPath);
      }
    }
  }

  /**
   * 删除单个文件
   *
   * @param sPath 被删除文件的文件名
   * @return 单个文件删除成功返回true，否则返回false
   */
  public static boolean deleteFile(String sPath) {
    boolean flag = false;
    File file = new File(sPath);
    // 路径为文件且不为空则进行删除
    if (file.isFile() && file.exists()) {
      file.delete();
      flag = true;
    }
    return flag;
  }

  /**
   * 删除目录（文件夹）以及目录下的文件
   *
   * @param sPath 被删除目录的文件路径
   * @return 目录删除成功返回true，否则返回false
   */
  public static boolean deleteDirectory(String sPath) {
    // 如果sPath不以文件分隔符结尾，自动添加文件分隔符
    if (!sPath.endsWith(File.separator)) {
      sPath = sPath + File.separator;
    }
    File dirFile = new File(sPath);
    // 如果dir对应的文件不存在，或者不是一个目录，则退出
    if (!dirFile.exists() || !dirFile.isDirectory()) {
      return false;
    }
    boolean flag = true;
    // 删除文件夹下的所有文件(包括子目录)
    File[] files = dirFile.listFiles();
    for (int i = 0; i < files.length; i++) {
      // 删除子文件
      if (files[i].isFile()) {
        flag = deleteFile(files[i].getAbsolutePath());
        if (!flag)
          break;
      } // 删除子目录
      else {
        flag = deleteDirectory(files[i].getAbsolutePath());
        if (!flag)
          break;
      }
    }
    if (!flag)
      return false;
    // 删除当前目录
    if (dirFile.delete()) {
      return true;
    } else {
      return false;
    }
  }
  // #endregion

  // #region AES部分
  private static final String ALGORITHM = "AES";
  private static String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";

  public static SecretKey generatKey() {
    KeyGenerator keyGenerator = null;
    try {
      keyGenerator = KeyGenerator.getInstance(ALGORITHM);
    } catch (Exception e) {
      System.out.println("generatKey:keyGenerator 出错");
      System.out.println(e);
      return null;
    }
    // Random
    SecureRandom secureRandom = new SecureRandom();
    keyGenerator.init(secureRandom);
    return keyGenerator.generateKey();
  }

  public static SecretKey generatKey(byte[] key) {
    return key.length == 16 ? new SecretKeySpec(key, ALGORITHM) : generatKey();
  }

  public static SecretKey generatKey(String key) {
    byte[] bk = null;
    bk = key.getBytes(StandardCharsets.UTF_8);
    if (bk.length != 16) {
      bk = Base64.getDecoder().decode(key);
    }
    return generatKey(bk);
  }

  static {
    try {
      Security.addProvider(new BouncyCastleProvider());
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  private static byte[] crypto(byte[] data, int mode, SecretKey secretKey, byte[] iv) {
    Cipher cipher = null;
    try {
      cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC");
    } catch (Exception e) {
      throw new RuntimeException("cipher 出错:" + errString(e));
    }
    try {
      cipher.init(mode, secretKey, new IvParameterSpec(iv));
    } catch (Exception e) {
      throw new RuntimeException("cipher.init 出错:" + errString(e));
    }
    try {
      byte[] ret = cipher.doFinal(data);
      return ret;
    } catch (Exception e) {
      throw new RuntimeException("cipher.doFinal 出错:" + errString(e));
    }
  }

  public static byte[] aes(byte[] data, String key, Object de) {
    return crypto(data, de == null ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, generatKey(key), new byte[16]);
  }

  public static byte[] aes(byte[] data, byte[] key, Object de) {
    return crypto(data, de == null ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, generatKey(key), new byte[16]);
  }

  public static byte[] aes(byte[] data, String key) {
    return crypto(data, Cipher.ENCRYPT_MODE, generatKey(key), new byte[16]);
  }

  public static byte[] aes(byte[] data, byte[] key) {
    return crypto(data, Cipher.ENCRYPT_MODE, generatKey(key), new byte[16]);
  }

  // #endregion

  // #region 数据处理
  public static String formatUrl(String url, String domain) {
    if (url == null) {
      url = "";
    }
    if (Util.isEmpty(domain)) {
      domain = "/";
    } else if (domain.indexOf(':') < 0 && domain.indexOf('.') >= 0) {
      domain = "https://" + domain;
    }
    if (url.indexOf('/') == 0) {
      url = String.format("%s%s", domain, url);
    } else if (url.indexOf("http") != 0) {
      url = String.format("%s/%s", domain, url);
    }

    return url;
  }

  public static String errString(Exception e) {
    // StackTraceElement[] st = e.getStackTrace();
    // if (st.length > 0) {
    // s += " 文件定位: " + st[0].getFileName() + ":" + st[0].getLineNumber();
    // }

    String s = null;
    s = e.getMessage();
    if (e instanceof InvocationTargetException) {
      s = ((InvocationTargetException) e).getTargetException().getMessage();
    }

    return s;
  }

  public static <T> T clone(T v, Class<T> clazz) {
    return JSON.parseObject(JSON.toJSONString(v), clazz);
  }

  /** 数字转汉字 */
  public static String numberToChinese(int number) {
    String ret = "", str = String.valueOf(number);
    int n = str.length();
    for (int i = 0; i < n; i++) {
      int num = str.charAt(i) - '0';
      if (i != n - 1 && num != 0) {
        ret += DIGITS[num] + UNITS[n - 2 - i];
      } else {
        ret += DIGITS[num];
      }
    }
    return ret.replaceAll("^一十", "十").replaceAll("零+$", "").replaceAll("零+", "零");
  }

  /** 查询到返回下标,否则返回-1 */
  public static int includes(String[] arr, String item) {
    if (arr.length > 0 && item != null) {
      for (int i = 0; i < arr.length; i++) {
        if (item.equals(arr[i])) {
          return i;
        }
      }
    }
    return -1;
  }

  public static boolean isEmpty(String str) {
    return str == null || str.length() < 1;
  }

  public static long getId() {
    IdWorker.initSequence(rand(0, 31), rand(0, 31));
    return IdWorker.getId();
  }

  public static String sGetId() {
    return sGetId32().substring(0, 16);
  }

  public static String sGetId36() {
    return UUID.randomUUID().toString();
  }

  public static String sGetId32() {
    return sGetId36().replace("-", "");
  }

  public static String sGetId40() {
    return toHash(sGetId32(), true);
  }

  public static JSONObject toJsonObject(Object p) {
    return JSON.parseObject(toJString(p));
  }

  public static String toJString(Object p) {
    return p != null
        ? JSON.toJSONString(p, JSONWriter.Feature.WriteLongAsString, JSONWriter.Feature.WriteBigDecimalAsPlain,
            JSONWriter.Feature.WriteByteArrayAsBase64, JSONWriter.Feature.WriteNullListAsEmpty)
        : "";
  }

  public static <T> T toJavaObject(JSONObject p, Class<T> clazz) {
    try {
      return p.toJavaObject(clazz, JSONReader.Feature.Base64StringAsByteArray);
    } catch (Exception e) {
      JSONObject t = p.getJSONObject("permission");
      int l = p.getSize("permission");
      byte[] b = new byte[l];
      for (int i = 0; i < l; i++) {
        b[i] = t.getByteValue(String.valueOf(i));
      }
      p.remove("permission");
      p.put("permission", b);
      return p.toJavaObject(clazz);
    }
  }

  public static JSONArray toArray(String param) {
    return JSON.parseArray(param, JSONReader.Feature.Base64StringAsByteArray);
  }

  public static <T> List<T> toArray(String p, Class<T> clazz) {
    return JSON.parseArray(p, clazz, JSONReader.Feature.Base64StringAsByteArray);
  }

  public static <T> int arrFindIndex(List<T> list, java.util.function.Predicate<T> predicate) {
    return list.stream().filter(predicate).mapToInt(i -> list.indexOf(i)).findFirst().orElse(-1);
  }

  public static int arrFindIndex(List<String> list, String p) {
    if (p == null || list == null) {
      return -1;
    }
    return arrFindIndex(list, e -> p.equals(e));
  }

  /**
   * 类似于JavaScript的find方法，返回列表中第一个满足predicate条件的元素。
   * 如果没有元素满足条件，则返回Optional.empty()。
   *
   * @param list      要搜索的列表
   * @param predicate 用于测试每个元素是否满足条件的Predicate
   * @return 第一个满足条件的元素，如果没有则为Optional.empty()
   */
  public static <T> T arrFind(List<T> list, java.util.function.Predicate<T> predicate) {
    return list.stream().filter(predicate).findFirst().orElse(null);
  }

  /**
   * 根据大写字母分割驼峰式字符串。
   * 
   * @param input 驼峰式字符串
   * @return 分割后的单词列表
   */
  public static List<String> splitByUpperCase(String input) {
    List<String> result = new ArrayList<>();
    StringBuilder currentWord = new StringBuilder();
    for (char ch : input.toCharArray()) {
      if (Character.isUpperCase(ch)) {
        if (currentWord.length() > 0) {
          result.add(currentWord.toString());
          currentWord.setLength(0);
        }
      }
      currentWord.append(ch);
    }
    // 添加最后一个单词（如果有的话）
    if (currentWord.length() > 0) {
      result.add(currentWord.toString());
    }
    return result;
  }

  public static boolean isNs(String p) {
    return p != null && p.matches("^[a-z_A-Z][\\w]+$");
  }

  public static boolean isNumeric(String str) {
    if (str == null) {
      return false;
    }
    // 正则表达式匹配负数、正数和浮点数
    String regex = "^[-+]?[0-9]+\\.?[0-9]+([eE][-+]?[0-9]+)?$";
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(str);
    return matcher.matches();
  }

  public static int countStrC(String str, char c) {
    int count = 0;
    if (str != null) {
      for (int i = 0; i < str.length(); i++) {
        if (str.charAt(i) == c) {
          count++;
        }
      }
    }
    return count;
  }

  public static String setStrChar(String str, int index, char c) {
    StringBuilder sb = new StringBuilder(str);
    sb.setCharAt(index, c);
    return sb.toString();
  }

  public static String genStr(int len, String str) {
    if (str == null) {
      str = "0";
    }
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < len; i++) {
      sb.append(str);
    }
    return sb.toString();
  }

  public static String getTable(Class<?> clazz, Iterable<? extends CharSequence> elements) {
    List<String> res = new ArrayList<>();
    String t = null;
    for (CharSequence charSequence : elements) {
      if (charSequence != null) {
        t = getColumnName(clazz, charSequence.toString());
        if (t != null) {
          res.add(t);
        }
      }
    }
    return res.size() > 0 ? String.join(",", res) : "*";
  }

  public static String getColumnName(Class<?> clazz, String propertyName) {
    try {
      Field field = clazz.getDeclaredField(propertyName);
      TableField tableField = field.getAnnotation(TableField.class);
      if (tableField == null) {
        return field.getName();
      } else {
        return tableField.value();
      }
    } catch (Exception e) {
    }
    return null;
  }

  public static <T> T listGet(List<T> list, int index) {
    try {
      return list.get(index);
    } catch (Exception e) {
    }
    return null;
  }

  public static <T> T listGet(List<T> list, String index) {
    return listGet(list, Integer.valueOf(index));
  }

  public static int listIndexOf(List<String> list, String param) {
    if (list != null) {
      int l = list.size();
      if (param == null) {
        for (int i = 0; i < l; i++) {
          if (list.get(i) == null) {
            return i;
          }
        }
      } else {
        String t = null;
        for (int i = 0; i < l; i++) {
          t = list.get(i);
          if (t != null && t.equals(param)) {
            return i;
          }
        }
      }
    }
    return -1;
  }

  public static int listIndexOf(List<Integer> list, Integer param) {
    if (list != null) {
      int l = list.size();
      if (param == null) {
        for (int i = 0; i < l; i++) {
          if (list.get(i) == null) {
            return i;
          }
        }
      } else {
        Integer t = null;
        for (int i = 0; i < l; i++) {
          t = list.get(i);
          if (t != null && t.equals(param)) {
            return i;
          }
        }
      }
    }
    return -1;
  }

  public static boolean listIncludes(List<String> list, String param) {
    return listIndexOf(list, param) >= 0;
  }

  public static boolean listIncludes(List<Integer> list, Integer param) {
    return listIndexOf(list, param) >= 0;
  }

  public static int long2int(long in) {
    try {
      return Math.toIntExact(in);
    } catch (Exception e) {
    }
    return -1;
  }

  public static byte[] getBytes(Object obj) {
    if (obj != null) {
      if (obj instanceof Integer) {
        return ByteBuffer.allocate(Integer.BYTES).putInt((Integer) obj).array();
      } else if (obj instanceof Long) {
        return ByteBuffer.allocate(Long.BYTES).putLong((Long) obj).array();
      } else if (obj instanceof Float) {
        return ByteBuffer.allocate(Float.BYTES).putFloat((Float) obj).array();
      } else if (obj instanceof Double) {
        return ByteBuffer.allocate(Double.BYTES).putDouble((Double) obj).array();
      } else if (obj instanceof Boolean) {
        return new byte[] { (byte) (Boolean.TRUE.equals(obj) ? 1 : 0) };
      } else if (obj instanceof String) {
        return ((String) obj).getBytes();
      } else if (obj instanceof Byte[]) {
        return (byte[]) obj;
      } else if (obj instanceof Byte) {
        return new byte[] { (byte) obj };
      }
      try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.flush();
        return baos.toByteArray();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    return null;
  }

  public static String toHash(Object obj) {
    return toHash(getBytes(obj), true);
  }

  public static String toHash(Object obj, boolean isSha1) {
    try {
      MessageDigest md = MessageDigest.getInstance(isSha1 ? "SHA-1" : "MD5");
      if (obj == null) {
        obj = sGetId();
      }
      md.update(getBytes(obj instanceof String ? obj : toJString(obj)));
      return String.format("%02X", new BigInteger(1, md.digest()));
    } catch (Exception e) {
      e.printStackTrace();
    }
    return "";
  }

  public static String byteToHex(byte b) {
    String hex = Integer.toHexString(b & 0xFF);
    if (hex.length() < 2) {
      hex = "0" + hex;
    }
    return hex;
  }

  public static long timeMillis() {
    return System.currentTimeMillis();
  }

  public static long time() {
    return System.currentTimeMillis() / 1000;
  }

  public static long time(String dateStr) {
    try {
      return OffsetDateTime.parse(dateStr).toInstant().toEpochMilli() / 1000;
    } catch (Exception e) {
    }
    return 0L;
  }

  public static long time(Date date) {
    return date.getTime() / 1000;
  }

  public static long time(LocalDate date) {
    return date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() / 1000;
  }

  public static long time(Calendar calendar) {
    return calendar.getTimeInMillis() / 1000;
  }

  public static String timeISO8601() {
    Instant now = Instant.now();
    DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
    return formatter.format(now.atOffset(ZoneOffset.of("+08:00")));
  }

  public static String sTime() {
    return String.valueOf(time());
  }

  public static String sTime(String dateStr) {
    try {
      return String.valueOf(Long.valueOf(OffsetDateTime.parse(dateStr).toInstant().toEpochMilli() / 1000));
    } catch (Exception e) {
    }
    return "0";
  }

  public static String sTime(Date date) {
    return String.valueOf(time(date));
  }

  public static String sTime(Calendar calendar) {
    return String.valueOf(time(calendar));
  }

  public static String l2times(long timestamp, String formatteString) {
    if (formatteString == null) {
      formatteString = "yyyy-MM-dd HH:mm:ss";
    }
    if (timestamp > 1000000000L && timestamp < 10000000000L) {
      timestamp *= 1000;
    }
    Instant instant = Instant.ofEpochMilli(timestamp);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatteString);
    return instant.atZone(ZoneId.systemDefault()).format(formatter);
  }

  public static String l2times(long timestamp) {
    return l2times(timestamp, null);
  }

  public static byte[] reverse(byte[] buffer) {
    int l = buffer.length, i = 0;
    byte[] ret = new byte[l];
    while (l-- > 0) {
      ret[i++] = buffer[l];
    }
    return ret;
  }

  public static byte[] randByte(int size) {
    byte[] r = new byte[size];
    Random rand = new Random(getId());
    for (int i = 0; i < size; i++) {
      r[i] = (byte) (rand.nextInt(0xff));
    }
    return r;
  }

  public static String randString(int size) {
    byte[] r = new byte[size];
    Random rand = new Random(getId());
    for (int i = 0; i < size; i++) {
      switch (rand.nextInt(3)) {
        case 0: // 数字
          r[i] = (byte) (rand.nextInt(9) + 48);
          break;
        case 1: // 大写字母
          r[i] = (byte) (rand.nextInt(25) + 65);
          break;
        case 2: // 小写字母
          r[i] = (byte) (rand.nextInt(25) + 97);
          break;

        default: // 全体字符/符号
          r[i] = (byte) (rand.nextInt(0x5e) + 0x20);
          break;
      }
    }
    try {
      return new String(r, "utf8");
    } catch (Exception e) {
      throw new RuntimeException("系统错误: 随机码");
    }
  }

  public static int rand(int max) {
    return new Random(getId()).nextInt(max);
  }

  public static int rand(int min, int max) {
    double random = Math.random() * 1000000000000000000L;
    DecimalFormat df = new DecimalFormat("0");
    return new Random(Long.valueOf(df.format(random))).nextInt((max - min) + 1) + min;
  }

  public static String b2s(byte[] data) {
    try {
      return new String(data, "utf8");
    } catch (Exception e) {
      throw new RuntimeException("b2s:" + errString(e));
    }
  }

  public static String Complement(int size, String cstr) {
    String ret = "";
    while (size-- > 0) {
      ret += cstr;
    }
    return ret;
  }

  public static long Placeholder(long value) {
    byte[] d = getBytes(value);
    String r = "0";
    for (int i = 0; i < d.length; i++) {
      if (d[i] != 0) {
        r = Complement(d.length - i, "ff");
      }
    }
    return Long.parseUnsignedLong(r, 16);
  }

  public static String getRequestBody(HttpServletRequest request) {
    try {
      BufferedReader reader = request.getReader();
      StringBuilder builder = new StringBuilder();
      String line;
      while ((line = reader.readLine()) != null) {
        builder.append(line);
      }
      reader.close();
      return builder.toString();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public static JSONObject request2json(HttpServletRequest request) {
    JSONObject ret = null;
    try {
      ret = JSONObject.parseObject(getRequestBody(request));
    } catch (Exception e) {
      ret = new JSONObject();
      Map<String, String[]> parameterMap = request.getParameterMap();
      for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
        ret.put(entry.getKey(), entry.getValue()[0]);
      }
    }
    return ret == null ? new JSONObject() : ret;
  }

  public static void clear() {
    System.out.print("\033[H\033[2J");
    System.out.flush();
  }

  public static void clear(Object... p) {
    System.out.print("\033[H\033[2J");
    System.out.flush();
    if (p.length > 0) {
      String o = "";
      for (Object object : p) {
        o += ", " + object.toString();
      }
      System.out.println(o.substring(2) + "\n");
    }
  }

  public static int s2i(String str) {
    if (str == null || str.length() == 0)
      return 0;
    char[] array = str.toCharArray();
    long result = 0; // 要返回的结果result
    int count = 0; // 记录‘+'或者‘-'出现的次数
    int num = 0; // 判断空格出现的位置
    int flag = 1; // 正数还是负数
    for (int i = 0; i < array.length; i++) {
      Character c = array[i];
      if (c >= '0' && c <= '9') {
        result = result * 10 + c - '0';
        // 判断是否溢出
        if (flag == 1 && result > Integer.MAX_VALUE) {
          return Integer.MAX_VALUE;
        } else if (flag == -1 && -result < Integer.MIN_VALUE)
          return Integer.MIN_VALUE;
        num++;
      } else if (c == ' ' && num == 0 && count == 0)
        continue;
      else if (c == '+' && count == 0) {
        count = 1;
      } else if (c == '-' && count == 0) {
        flag = -1;
        count = 1;
      } else {
        return (int) (flag * result);

      }
    }
    return (int) (flag * result);
  }

  public static long s2l(String str) {
    if (str == null || str.length() == 0) {
      return 0;
    }
    char[] array = str.toCharArray();
    long result = 0; // 要返回的结果result
    long count = 0; // 记录‘+'或者‘-'出现的次数
    long num = 0; // 判断空格出现的位置
    long flag = 1; // 正数还是负数
    for (int i = 0; i < array.length; i++) {
      Character c = array[i];
      if (c >= '0' && c <= '9') {
        result = result * 10 + c - '0';
        // 判断是否溢出
        if (flag == 1 && result > Long.MAX_VALUE) {
          return Long.MAX_VALUE;
        } else if (flag == -1 && -result < Long.MIN_VALUE)
          return Long.MIN_VALUE;
        num++;
      } else if (c == ' ' && num == 0 && count == 0)
        continue;
      else if (c == '+' && count == 0) {
        count = 1;
      } else if (c == '-' && count == 0) {
        flag = -1;
        count = 1;
      } else {
        return (long) (flag * result);
      }
    }
    return (long) (flag * result);
  }

  public static boolean isNumber(Object d) {
    String s = d.toString();
    if ((s.indexOf("'") == 0 && s.indexOf("'", 1) == s.length() - 1)
        || (s.indexOf("\"") == 0 && s.indexOf("\"", 1) == s.length() - 1)) {
      s = s.substring(1, s.length() - 1);
    }
    int bit;
    if (s.indexOf("0x") == 0) {
      bit = 16;
      s = s.substring(2);
    } else {
      bit = 10;
    }
    while (s.length() > 1 && s.indexOf("0") == 0) {
      s = s.substring(1);
    }
    try {
      return s.equals(new BigInteger(s).toString(bit));
    } catch (Exception e) {
      return false;
    }
  }

  public static String secure(String str) {
    return str == null ? str : str.replaceAll("(['\\\\])", "\\\\$1");
  }

  public static boolean isSafeString(String s) {
    boolean ret = false;
    if (s != null) {
      if (s.charAt(0) == '\'') {
        int i = 1, l = s.length() - 1;
        if (s.charAt(l) == '\'') {
          char c;
          while (i < l) {
            c = s.charAt(i);
            if (c == '\\') {
              i++;
            } else if (c == '\'') {
              break;
            }
            i++;
          }
          ret = i == l;
        }
      } else {
        ret = isNumber(s);
      }
    }
    return ret;
  }

  public static String formatRemarks(String dist, String source, String create) {
    String def = "{\"remarks\":[]}";
    if (dist == null || dist.indexOf("{") != 0) {
      dist = def;
    }
    JSONObject d, s;
    try {
      d = JSONObject.parseObject(dist);
    } catch (Exception e) {
      d = JSONObject.parseObject(def);
    }
    JSONArray dr = d.getJSONArray("remarks"), sr;
    if (dr == null) {
      dr = new JSONArray();
      d.put("remarks", dr);
    }
    if (source != null) {
      try {
        s = JSONObject.parseObject(source);
      } catch (Exception e) {
        throw new RuntimeException("字段 other 非法");
      }
      if (s != null) {
        sr = s.getJSONArray("remarks");
        if (sr != null && sr.size() > 0) {
          List<Remark> lr = sr.toJavaList(Remark.class);
          if (lr != null && lr.size() > 0) {
            Remark e = lr.get(lr.size() - 1);
            dr.add(e);
            d.put("remarks", dr);
          }
        }
      }
    }
    return toJString(d);
  }

  public static List<String> split(String s, String k) {
    if (s == null || k == null || s.length() < k.length()) {
      return null;
    }
    List<String> ret = new ArrayList<>();
    int i = s.indexOf(k), l = 0, kl = k.length();
    if (i < 0) {
      ret.add(s);
    } else {
      do {
        ret.add(s.substring(l, i));
        l = i + kl;
        i = s.indexOf(k, l);
      } while (i > 0);
      ret.add(s.substring(l));
    }
    return ret;
  }

  public static byte[] zip(String input) {
    return zip(input, 9);
  }

  public static byte[] zip(String input, int level) {
    try {
      Deflater deflater = new Deflater(level);
      deflater.setInput(input.getBytes("UTF-8"));
      deflater.finish();

      ByteArrayOutputStream outputStream = new ByteArrayOutputStream(input.length());
      byte[] buffer = new byte[4096];
      while (!deflater.finished()) {
        int count = deflater.deflate(buffer); // Compresses
        outputStream.write(buffer, 0, count);
      }
      outputStream.close();
      byte[] output = outputStream.toByteArray();
      deflater.end();
      return output;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public static byte[] unzip(byte[] input) {
    try {
      Inflater inflater = new Inflater();
      inflater.setInput(input);

      // 使用ByteArrayInputStream和InflaterInputStream来解压缩数据
      InflaterInputStream inputStream = new InflaterInputStream(new ByteArrayInputStream(input), inflater);
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

      byte[] buffer = new byte[1024];
      int len;
      while ((len = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, len);
      }

      // 关闭流
      inputStream.close();
      outputStream.close();
      return outputStream.toByteArray();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public static String sunzip(byte[] input) {
    try {
      Inflater inflater = new Inflater();
      inflater.setInput(input);

      // 使用ByteArrayInputStream和InflaterInputStream来解压缩数据
      InflaterInputStream inputStream = new InflaterInputStream(new ByteArrayInputStream(input), inflater);
      InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);

      StringBuilder decompressedStringBuilder = new StringBuilder();
      char[] buffer = new char[1024];
      int len;
      while ((len = reader.read(buffer)) != -1) {
        decompressedStringBuilder.append(buffer, 0, len);
      }

      // 关闭流
      reader.close();
      inputStream.close();

      return decompressedStringBuilder.toString();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /** 驼峰 */
  public static String UnderlineHump(String in) {
    return UnderlineHump(in, false);
  }

  public static String UnderlineHump(String in, Boolean fristUpper) {
    String r = in;
    if (in != null) {
      // int i, l = r.length();
      // while ((i = r.indexOf("_")) >= 0 && i < --l) {
      // r = r.substring(0, i) + r.substring(++i, ++i).toUpperCase() + (l >= i ?
      // r.substring(i) : "");
      // }
      // r = r.substring(0, 1).toLowerCase() + r.substring(1);
      java.util.regex.Matcher m = java.util.regex.Pattern.compile("[_\\-]+(\\w)").matcher(r);
      StringBuffer sb = new StringBuffer();
      while (m.find()) {
        m.appendReplacement(sb, m.group(1).toUpperCase());
      }
      if (sb.length() > 0) {
        char c = sb.charAt(0);
        if (fristUpper != null && fristUpper) {
          if (c >= 'a' && c <= 'z') {
            c -= 32;
          }
        } else {
          if (c >= 'A' && c <= 'Z') {
            c += 32;
          }
        }
        sb.setCharAt(0, c); // 首字母 大小写转换
        m.appendTail(sb);
        r = sb.toString();
      }
    }
    return r;
  }

  /** 首字母小写 */
  public static String FristLower(String in) {
    String r = "";
    if (in != null) {
      StringBuffer sb = new StringBuffer(in);
      char c = sb.charAt(0);
      if (c >= 'A' && c <= 'Z') {
        sb.setCharAt(0, (char) (c + 32));
      }
      r = sb.toString();
    }
    return r;
  }

  public static <T> JSONObject StaticClassToJsonString(Class<T> clazz) {
    JSONObject r = new JSONObject();
    String n;
    for (Field field : clazz.getDeclaredFields()) {
      n = field.getName();
      try {
        r.put(UnderlineHump(n), field.get(n));
      } catch (Exception e) {
      }
    }
    return r;
  }

  public static String getUUID() {
    return getUUID(false);
  }

  public static String getUUID(boolean simple) {
    ThreadLocalRandom random = ThreadLocalRandom.current();
    String ret = (new UUID(random.nextLong(), random.nextLong())).toString();
    // System.out.println(ret + " " + random.nextLong() + " " + random.nextLong());
    return simple ? ret.replace("-", "") : ret;
  }
  // #endregion

  // #region mybatisPlus
  // #region 原
  public static <T> Page<T> getPage(JSONObject p, Class<T> clazz) {
    Page<T> ret = null;
    try {
      if (p.containsKey("#page")) {
        ret = new Page<T>(1, 10);
        try {
          JSONObject page = p.getJSONObject("#page");
          Long t;
          if ((t = page.getLong("current")) >= 0) {
            ret.setCurrent(t);
          }
          if ((t = page.getLong("size")) > 0) {
            if (t > 2000) {
              t = 2000l;
            }
            ret.setSize(t);
          }
        } catch (Exception e) {
        }
      }
    } catch (Exception e) {
    }
    return ret;
  }

  public static String safeString(Object param) {
    String ps = param == null ? ""
        : (param instanceof String ? (String) param
            : JSON.toJSONString(param,
                JSONWriter.Feature.WriteBigDecimalAsPlain,
                JSONWriter.Feature.WriteByteArrayAsBase64, JSONWriter.Feature.WriteNullListAsEmpty));
    return ps.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'");
  }

  public static String safeString(Object param, boolean quote) {
    String r = safeString(param);
    return quote ? ("'" + r + "'") : r;
  }

  private static int getLetterIndex(byte bs[]) {
    if (bs != null) {
      int l = bs.length;
      for (int i = 0; i < l; i++) {
        // if ((b > 126 || b < 20) || (b > 64 && b < 123 && b != 94 /* 94: 排出符号 ^ */)) {
        if ((bs[i] > 64 && bs[i] < 91) || (bs[i] > 96 && bs[i] < 123)) { // 字母区间
          return i;
        }
      }
    }
    return -1;
  }

  public static int getLetterIndex(String s) {
    return s == null ? -1 : getLetterIndex(s.getBytes());
  }

  public static boolean checkSafeSql(String p) {
    if (p != null) {
      byte bs[] = p.getBytes(), b = 0;
      boolean lb = true;
      int i = 0, len = bs.length;
      while (i < len) {
        if (bs[i] != '\\') { // 排除转义符
          if (lb) { // 判断引号外 (非字符串内容)
            if (bs[i] == '\'' || bs[i] == '"') { // 找到起始符号
              b = bs[i]; // 设定符号值
              lb = false;
            } else if ((bs[i] >= 'A' && bs[i] <= 'Z') || (bs[i] >= 'a' && bs[i] <= 'z')) { // 有字母
              return false;
            }
          } else if (bs[i] == b) { // 寻找结束符 此时,i 一定是大于0
            lb = true; // 本次字符串完毕
            b = 0; // 留作循环结束后判定符号是否匹配
          }
        }
        i++;
      }
      return b == 0;
    }
    return false;
  }

  public static String safeSql(String sql, String key) {
    if (sql != null) {
      String ret = "";
      byte bs[] = sql.getBytes(), b = 0;
      boolean lb = true, replace = key != null;
      int i = 0, len = bs.length, last = 0;
      while (i < len) {
        if (bs[i] != '\\') { // 排除转义符
          if (lb) { // 判断引号外 (非字符串内容)
            if (bs[i] == '#') {
              if (replace) {
                ret += sql.substring(last, i) + key;
                last = i + 1;
              }
            } else if (bs[i] == '\'' || bs[i] == '"' || bs[i] == '`') { // 找到起始符号 || bs[i] == '`' 此规则容易导致全线超限 不过查询无所谓了
              b = bs[i]; // 设定符号值
              lb = false;
            } else if ((bs[i] >= 'A' && bs[i] <= 'Z') || (bs[i] >= 'a' && bs[i] <= 'z')) { // 有字母
              return null;
            }
          } else if (bs[i] == b) { // 寻找结束符 此时,i 一定是大于0
            lb = true; // 本次字符串完毕
            b = 0; // 留作循环结束后判定符号是否匹配
          }
        } else { // 再加1;
          i++;
        }
        i++;
      }
      if (b == 0) {
        return ret + sql.substring(last);
      }
    }
    return null;
  }

  public static <T> QueryWrapper<T> setWrapper(QueryWrapper<T> ret, String key, Object d) {
    if (ret != null && key != null && d != null && key.length() > 0) {
      String column = key.indexOf("`") == 0 ? key : ("`" + key + "`"), v = d.toString();
      final int likeL = 5, regexpL = 7, applyL = 6;
      if (v.indexOf("![") == 0) {
        try {
          List<String> a = toArray(v.substring(1), String.class);
          if (a.size() > 0) {
            ret.in(column, a);
          }
        } catch (Exception e) {
        }
      } else if (v.indexOf("[") == 0) {
        try {
          List<String> a = toArray(v, String.class);
          if (a.size() > 0) {
            ret.in(column, a);
          }
        } catch (Exception e) {
        }
      } else if (v.indexOf("apply ") == 0) {
        v = safeSql(v.substring(applyL), column);
        if (v != null) {
          ret.apply(v);
        }
      } else if (v.indexOf("regexp ") == 0) {
        ret.apply(String.format("%s REGEXP '%S'", column, v.substring(regexpL).replaceAll("(['\\\\])", "\\\\$1")));
      } else if (v.indexOf("like ") == 0) { // like
        ret.like(column, v.substring(likeL));
      } else if (v.equals("null")) {
        ret.isNull(column);
      } else if (v.equals("!null") || v.equals("not null")) {
        ret.isNotNull(column);
      } else {
        ret.eq(column, v);
      }
    }
    return ret;
  }

  public static <T> QueryWrapper<T> setWrapper(QueryWrapper<T> ret, String key, JSONObject p) {
    setWrapper(ret, key, p.get(key));
    return ret;
  }

  public static <T> QueryWrapper<T> setWrapper(QueryWrapper<T> ret, String[] key, JSONObject p) {
    for (String s : key) {
      setWrapper(ret, s, p.get(s));
    }
    return ret;
  }

  // #endregion 原
  // #region 添加基础规则部分
  private static String safeValue(Class<?> clazz, String value, String prefix) {
    String ret = null;
    if (isNumeric(value)) {
      ret = value;
    } else if (value.indexOf('#') == 0) {
      ret = getColumnName(clazz, value.substring(1));
    } else {
      ret = getColumnName(clazz, value);
    }
    if (ret == null) {
      ret = "'" + safeString(value) + "'";
    }
    return ret;
  }

  private static <T> void setLogic(Class<?> clazz, QueryWrapper<T> q, String field, String logicStr, String value,
      String prefix) {
    field = getColumnName(clazz, field);
    if (field == null) {
      throw new RuntimeException("字段" + field + "不存在");
    }
    List<String> va = null;
    String[] sa = null;
    String s = safeValue(clazz, value, prefix);
    try {
      va = toArray(value, String.class);
    } catch (Exception e) {
    }
    field = prefix + field;
    switch (logicStr.trim().toLowerCase()) {
      case "=":
      case "eq":
        q.apply(String.format("%s=%s", field, s));
        break;
      case "<>":
      case "ne":
        q.apply(String.format("%s<>%s", field, s));
        break;
      case ">":
      case "gt":
        q.apply(String.format("%s>%s", field, s));
        break;
      case ">=":
      case "ge":
        q.apply(String.format("%s>=%s", field, s));
        break;
      case "<":
      case "lt":
        q.apply(String.format("%s<%s", field, s));
        break;
      case "<=":
      case "le":
        q.apply(String.format("%s<=%s", field, s));
        break;
      case "between":
        if (value.indexOf('-') >= 0) {
          sa = value.split("-");
          q.between(field, sa[0], sa[1]);
        } else if (va != null && va.size() == 2) {
          q.between(field, va.get(0), va.get(1));
        } else {
          q.isNull(field).isNotNull(field);
        }
        break;
      case "not between":
      case "notbetween":
        if (value.indexOf('-') >= 0) {
          sa = value.split("-");
          q.notBetween(field, sa[0], sa[1]);
        } else if (va != null && va.size() == 2) {
          q.notBetween(field, va.get(0), va.get(1));
        } else {
          q.isNull(field).isNotNull(field);
        }
        break;
      case "like":
        if (value.indexOf('%') >= 0) {
          q.apply(String.format("%s like %s", field, s));
        } else {
          q.like(field, value);
        }
        break;
      case "not like":
      case "notlike":
        if (value.indexOf('%') >= 0) {
          q.apply(String.format("%s not like %s", field, s));
        } else {
          q.notLike(field, value);
        }
        break;
      case "is null":
      case "isnull":
        q.isNull(field);
        break;
      case "is not null":
      case "isnotnull":
        q.isNotNull(field);
        break;
      case "in":
        if (va != null && va.size() > 0) {
          q.in(field, va);
        } else {
          q.isNull(field).isNotNull(field);
        }
        break;
      case "not in":
      case "notin":
        if (va != null && va.size() > 0) {
          q.notIn(field, va);
        } else {
          q.isNull(field).isNotNull(field);
        }
        break;
      case "regexp":
        q.apply(String.format("%s REGEXP '%s'", field, value.replaceAll("(['\\\\])", "\\\\")));
        break;
      case "<=>":
        q.apply(String.format("%s<=>%s", field, s));
        break;
      case "&":
      case "|":
      case "^":
      case "~":
        q.apply(String.format("%s%s%s", field, logicStr, Long.parseLong(s)));
        break;
      default:
        break;
    }
  }

  private static <T> void appendRule(Class<?> clazz, QueryWrapper<T> q, JSONArray p, String prefix) {
    if (p == null || p.size() != 4) {
      return;
    }
    String field = p.getString(1), logic = p.getString(2), value = p.getString(3);
    try {
      if (p.getIntValue(0) == 0) {
        q.and(e -> setLogic(clazz, e, field, logic, value, prefix));
      } else {
        q.or(e -> setLogic(clazz, e, field, logic, value, prefix));
      }
    } catch (Exception e) {
    }
  }

  private static <T> void appendRules(Class<?> clazz, QueryWrapper<T> q, JSONArray p, String prefix) {
    if (p != null && p.size() == 2) {
      JSONArray rules = p.getJSONArray(1);
      if (p.getIntValue(0) == 0) {
        q.and(e -> {
          for (int i = 0, l = rules.size(); i < l; i++) {
            appendRule(clazz, e, rules.getJSONArray(i), prefix);
          }
        });
      } else {
        q.or(e -> {
          for (int i = 0, l = rules.size(); i < l; i++) {
            appendRule(clazz, e, rules.getJSONArray(i), prefix);
          }
        });
      }
    }
  }

  private static <T> void setRule(Class<?> clazz, QueryWrapper<T> q, Rule e, String prefix) {
    setLogic(clazz, q, e.field, e.logic, e.value, prefix);
    if (e.children != null && e.children.size() > 0) {
      setRules(clazz, q, e.children, prefix);
    }
  }

  private static <T> void setRules(Class<?> clazz, QueryWrapper<T> q, List<Rule> rules, String prefix) {
    if (rules != null) {
      rules.forEach(e -> {
        try {
          if (e.or == 0) {
            q.and(qe -> setRule(clazz, qe, e, prefix));
          } else {
            q.or(qe -> setRule(clazz, qe, e, prefix));
          }
        } catch (Exception er) {
        }
      });
    }
  }

  public static <T> QueryWrapper<T> getQuery(JSONObject p, Class<T> clazz) {
    return getQuery(p, clazz, null);
  }

  // #endregion 添加基础规则部分
  public static String formatQueryTablePrefix(String prefix) {
    if (prefix == null) {
      prefix = "";
    }
    if (prefix.length() > 0 && prefix.indexOf('.') < 0) {
      prefix += ".";
    }
    return prefix;
  }

  public static <T> QueryWrapper<T> getQuery(JSONObject p, Class<T> clazz, String prefix) {
    prefix = formatQueryTablePrefix(prefix);
    QueryWrapper<T> ret = new QueryWrapper<T>();
    String s, f;
    JSONArray ja, b;
    if (p != null && clazz != null) {
      if (p.containsKey("id") && (s = p.getString("id")).length() > 0) {
        try {
          ret.in(prefix + "id", toArray(s, String.class));
        } catch (Exception e) {
          ret.eq(prefix + "id", s);
        }
      } else {
        if (p.containsKey("#rule")) {
          try {
            setRules(clazz, ret, p.getJSONArray("#rule").toJavaList(Rule.class), prefix);
          } catch (Exception e) {
            throw new RuntimeException("查询条件设置错误:" + errString(e));
          }
        } else if (p.containsKey("#query")) {
          try {
            JSONArray q = p.getJSONArray("#query");
            for (int i = 0, l = q.size(); i < l; i++) {
              appendRules(clazz, ret, q.getJSONArray(i), prefix);
            }
          } catch (Exception e) {
            throw new RuntimeException("查询条件设置错误:" + errString(e));
          }
        }
        for (Field field : clazz.getDeclaredFields()) {
          f = field.getName();
          if (!f.equals("id")) {
            s = p.getString(f);
            f = getColumnName(clazz, f);
            if (s != null) {
              if (s.indexOf('%') >= 0) {
                ret.apply(String.format("%s like '%s'", f, s));
              } else {
                ret.eq(f, s);
              }
            }
          }
        }
      }
      try { // #order
        ja = p.getJSONArray("#order");
        if (ja != null) {
          for (int i = 0, l = ja.size(); i < l; i++) {
            b = ja.getJSONArray(i);
            s = getColumnName(clazz, b.getString(0));
            if (s != null) {
              if (b.getString(1).toUpperCase().equals("ASC")) {
                ret.orderByAsc(s);
              } else {
                ret.orderByDesc(s);
              }
            }
          }
        }
      } catch (Exception e) {
      }
      try { // #group
        p.getJSONArray("#group").toJavaList(String.class).forEach(e -> {
          String t = getColumnName(clazz, e);
          if (t != null) {
            ret.groupBy(t);
          }
        });
      } catch (Exception e) {
      }
      try { // #field
        List<String> fa = new ArrayList<>();
        p.getJSONArray("#field").toJavaList(String.class).forEach(e -> {
          if (!e.equals("password")) {
            List<String> tsa = Arrays.asList(e.trim().split("(\\s+as\\s+)|(\\s+)"));
            String t;
            if (tsa.size() == 2) {
              t = getColumnName(clazz, tsa.get(1));
              if (t != null && isNs(tsa.get(0))) {
                fa.add(String.format("%s as `%s`", t, tsa.get(0)));
              }
            } else {
              t = getColumnName(clazz, tsa.get(0));
              if (t != null) {
                fa.add(t);
              }
            }
          }
        });
        if (fa.size() > 0) {
          ret.select(String.join(",", fa));
        }
      } catch (Exception e) {
      }
    }
    return ret;
  }

  public static <T> QueryWrapper<T> getParentWrapper(String pid, String field) {
    return new QueryWrapper<T>().select("id").eq(field, pid);
  }

  public static <T> List<String> getParent(BaseMapper<T> mapper, String id) {
    List<String> ret = new ArrayList<>();
    String pid = id;
    List<T> t;
    JSONObject j;
    while (pid != null && pid.length() > 0) {
      ret.add(0, pid);
      t = mapper.selectList(new QueryWrapper<T>().select("pid").eq("id", pid));
      if (t == null || t.size() == 0) {
        break;
      }
      j = (JSONObject) JSON.toJSON(t.get(0));
      pid = j.getString("pid");
    }
    int l = ret.size();
    if (l > 0) {
      ret.remove(l - 1);
    }
    return ret;
  }

  public static <T> boolean isChildren(BaseMapper<T> mapper, String pid, String cid) {
    if (pid != null && pid.length() > 0) {
      List<T> t;
      while (cid != null && cid.length() > 0) {
        if (pid.equals(cid)) {
          return true;
        }
        t = mapper.selectList(new QueryWrapper<T>().select("pid").eq("id", cid));
        if (t == null || t.size() == 0) {
          break;
        }
        cid = ((JSONObject) JSON.toJSON(t.get(0))).getString("pid");
      }
    }
    return false;
  }

  public static <T> T getEntity(JSONObject p, Class<T> clazz) {
    // return p.containsKey("#entity") ?
    // p.getJSONObject("#entity").toJavaObject(clazz) : p.toJavaObject(clazz);
    return p.containsKey("#entity") ? toJavaObject(p.getJSONObject("#entity"), clazz) : toJavaObject(p, clazz);
  }

  public static <T> T getEntity(JSONObject p, Class<T> clazz, String cid) {
    T obj = getEntity(p, clazz);
    if (obj != null && cid != null) {
      try {
        Field field = clazz.getDeclaredField("create");
        field.setAccessible(true); // 如果字段是私有的，需要设置可访问
        if (field.getType().equals(String.class)) {
          field.set(obj, cid);
        }
      } catch (Exception e) {
      }
    }
    return obj;
  }

  public static <T> String insertSql(List<T> list, Class<T> clazz) {
    List<String> a = new ArrayList<>();
    StringBuilder r = new StringBuilder();
    TableName tn = clazz.getAnnotation(TableName.class);
    if (tn == null) {
      throw new RuntimeException("当前类型不是数据库实体");
    }
    r.append("INSERT INTO " + tn.value() + " (");
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
      TableField tf = field.getAnnotation(TableField.class);
      if (tf != null && tf.exist()) {
        r.append(" ," + tf.value());
        a.add(field.getName());
      }
      TableId ti = field.getAnnotation(TableId.class);
      if (ti != null) {
        r.append(ti.value());
        a.add(field.getName());
      }
    }
    r.append(") VALUES");
    for (T e : list) {
      r.append("\n  (");
      for (String k : a) {
        try {
          Field f = clazz.getDeclaredField(k);
          f.setAccessible(true);
          r.append(safeString(f.get(e), true) + ",");
        } catch (Exception _e) {
        }
      }
      r.setCharAt(r.length() - 1, ')');
      r.append(',');
    }
    r.setCharAt(r.length() - 1, ';');
    return r.toString();
  }
  // #endregion

  // #region mime
  private static int findString(String[] strAry, String str) {
    for (int i = 0; i < strAry.length; i++) {
      if (str.equals(strAry[i])) {
        return i;
      }
    }
    return -1;
  }

  private static String[] html = { "html", "htm", "shtml" };
  private static String[] text = { "css", "xml", "mml", "txt", "jad", "wml", "htc" };
  private static String[] img = { "xbm", "tif", "jfif", "ico", "tiff", "gif", "svg", "jpeg", "svgz", "jpg", "webp",
      "png", "bmp", "pjp", "apng", "pjpeg", "avif", "wbmp", "jng" };
  private static String[] audio = { "mid", "midi", "kar", "mp3", "ogg", "m4a", "ra", "aac" };
  private static String[] video = { "3gpp", "3gp", "mp4", "mpeg", "mpg", "mov", "webm", "flv", "m4v", "mng", "asf",
      "wmv", "avi" };
  private static String[] tjs = { "js", "mjs" };

  public static String mime(String str) {
    String ret = null;
    if (findString(html, str) >= 0) {
      ret = "text/html;charset=utf-8";
    } else if (findString(text, str) >= 0) {
      ret = "text/" + str + ";charset=utf-8";
    } else if (findString(img, str) >= 0) {
      ret = "image/" + str;
    } else if (findString(audio, str) >= 0) {
      ret = "audio/" + str;
    } else if (findString(video, str) >= 0) {
      ret = "video/" + str;
    } else if (findString(tjs, str) >= 0) {
      ret = "text/javascript";
    } else {
      switch (str) {
        case "js":
          ret = "application/javascript;charset=utf-8";
          break;
        case "woff":
          ret = "font/woff";
          break;
        case "woff2":
          ret = "font/woff2";
          break;
        case "ts":
          ret = "video/mp2t";
          break;
        case "m3u8":
          ret = "application/vnd.apple.mpegurl";
          break;

        default:
          ret = "application/" + str;
          break;
      }
    }

    return ret;
  }
  // #endregion

  // #region 获取私有属性
  public static Object getPrivate(Object s, String n) {
    Class<?> cls = s.getClass();
    try {
      Field field = cls.getDeclaredField(n);
      field.setAccessible(true);
      return field.get(s);
    } catch (Exception e) {
      // 获取属性错误
      System.out.println("getPrivate:获取属性错误");
    }

    return null;
  }

  public static Object getPrivate(Object s, String[] n) {
    Object ret = s;
    for (String nd : n) {
      ret = getPrivate(ret, nd);
      if (ret == null) {
        break;
      }
    }

    return ret;
  }
  // #endregion

  // #region 网络请求
  public static byte[] httpGet(String spec) {
    HttpURLConnection httpURLConnection = null;
    try {
      // 1. 得到访问地址的URL
      // URL url = new URL(spec);
      URL url = new URI(spec).toURL();
      // 2. 得到网络访问对象java.net.HttpURLConnection
      httpURLConnection = (HttpURLConnection) url.openConnection();
      /* 3. 设置请求参数（过期时间，输入、输出流、访问方式），以流的形式进行连接 */
      // 设置是否向HttpURLConnection输出
      httpURLConnection.setDoOutput(false);
      // 设置是否从httpUrlConnection读入
      httpURLConnection.setDoInput(true);
      // 设置请求方式 默认为GET
      httpURLConnection.setRequestMethod("GET");
      // 设置是否使用缓存
      httpURLConnection.setUseCaches(true);
      // 设置此 HttpURLConnection 实例是否应该自动执行 HTTP 重定向
      httpURLConnection.setInstanceFollowRedirects(true);
      // 设置超时时间
      httpURLConnection.setConnectTimeout(3000);
      // 连接
      httpURLConnection.connect();
      // 4. 得到响应状态码的返回值 responseCode
      int code = httpURLConnection.getResponseCode();
      // 5. 如果返回值正常，数据在网络中是以流的形式得到服务端返回的数据
      String msg = "";
      if (code == 200) { // 正常响应
        // // 从流中读取响应信息
        // BufferedReader reader = new BufferedReader(new
        // InputStreamReader(httpURLConnection.getInputStream()));
        // String line = null;
        // while ((line = reader.readLine()) != null) { // 循环从流中读取
        // msg += line + "\n";
        // }
        // reader.close(); // 关闭流
        InputStream inputStream = httpURLConnection.getInputStream();
        byte[] buff = new byte[4096];
        int len;
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        while ((len = inputStream.read(buff)) > 0) {
          os.write(buff, 0, len);
        }
        return os.toByteArray();
      }
      // 显示响应结果
      System.out.println("httpGet:" + msg);
    } catch (Exception e) {
      System.out.println("httpGet:转发出错，错误信息：" + e.getLocalizedMessage() + ";" + e.getClass());
    } finally {
      // 6. 断开连接，释放资源
      if (null != httpURLConnection) {
        try {
          httpURLConnection.disconnect();
        } catch (Exception e) {
          System.out.println("httpGet:httpURLConnection 流关闭异常：" + e.getLocalizedMessage());
        }
      }
    }
    throw new RuntimeException("网络异常");
  }

  public static String httpGet(String url, JSONObject params) {
    if (params != null) {
      List<String> sa = new ArrayList<>();
      for (String key : params.keySet()) {
        sa.add(key + "=" + URLEncoder.encode(params.getString(key), StandardCharsets.UTF_8));
      }
      if (sa.size() > 0) {
        url += "?" + String.join("&", sa);
      }
    }
    return new String(httpGet(url));
  }

  public static String httpPost(String postUrl, JSONObject params) {
    HttpURLConnection httpURLConnection = null;
    String ret = "";
    try {
      // 1. 获取访问地址URL
      // URL url = new URL("http://localhost:8080/Servlet/do_login.do");
      URL url = new URI(postUrl).toURL();
      // 2. 创建HttpURLConnection对象
      httpURLConnection = (HttpURLConnection) url.openConnection();
      /* 3. 设置请求参数等 */
      // 请求方式 默认 GET
      httpURLConnection.setRequestMethod("POST");
      // 超时时间
      httpURLConnection.setConnectTimeout(3000);
      // 设置是否输出
      httpURLConnection.setDoOutput(true);
      // 设置是否读入
      httpURLConnection.setDoInput(true);
      // 设置是否使用缓存
      httpURLConnection.setUseCaches(false);
      // 设置此 HttpURLConnection 实例是否应该自动执行 HTTP 重定向
      httpURLConnection.setInstanceFollowRedirects(true);
      // 设置请求头
      httpURLConnection.addRequestProperty("sysId", "sysId");
      // 设置使用标准编码格式编码参数的名-值对
      httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
      // 连接
      httpURLConnection.connect();
      /* 4. 处理输入输出 */
      // 写入参数到请求中
      OutputStream out = httpURLConnection.getOutputStream();
      if (params != null) {
        out.write(params.toJSONString().getBytes());
      }
      // 简化
      // httpURLConnection.getOutputStream().write(params.getBytes());
      out.flush();
      out.close();
      // 从连接中读取响应信息
      int code = httpURLConnection.getResponseCode();
      if (code == 200) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
        String line;
        while ((line = reader.readLine()) != null) {
          ret += line + "\n";
        }
        reader.close();
      }
    } catch (Exception e) {
      System.out.println("httpPost:转发出错，错误信息：" + e.getLocalizedMessage() + ";" + e.getClass());
    } finally {
      // 5. 断开连接
      if (null != httpURLConnection) {
        try {
          httpURLConnection.disconnect();
        } catch (Exception e) {
          System.out.println("httpPost:httpURLConnection 流关闭异常：" + e.getLocalizedMessage());
        }
      }
    }
    return ret;
  }
  // #endregion
}
